package com.demo.zyt.modules.app.controller;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.demo.zyt.common.dto.wechat.PurchaseSuccessDTO;
import com.demo.zyt.common.dto.wechat.TemplateResultDTO;
import com.demo.zyt.common.exception.RRException;
import com.demo.zyt.common.utils.AppResultEnum;
import com.demo.zyt.common.utils.IPUtils;
import com.demo.zyt.common.utils.R;
import com.demo.zyt.common.utils.SmsUtils;
import com.demo.zyt.config.CommonParameters;
import com.demo.zyt.config.ConfigConstant;
import com.demo.zyt.modules.app.annotation.Login;
import com.demo.zyt.modules.app.config.AccessTokenStore;
import com.demo.zyt.modules.app.entity.WxInviteEntity;
import com.demo.zyt.modules.app.entity.WxOrderEntity;
import com.demo.zyt.modules.app.entity.WxUserEntity;
import com.demo.zyt.modules.app.service.SysConfigService;
import com.demo.zyt.modules.app.service.WxOrderService;
import com.demo.zyt.modules.app.service.WxUserService;
import com.demo.zyt.modules.app.utils.JwtUtils;
import com.demo.zyt.modules.app.utils.WeChatUtils;
import com.demo.zyt.modules.app.wechatsdk.WXPay;
import com.demo.zyt.modules.app.wechatsdk.WXPayConstants;
import com.demo.zyt.modules.app.wechatsdk.WXPayConstants.SignType;
import com.demo.zyt.modules.app.wechatsdk.WXPayUtil;
import com.demo.zyt.modules.app.wechatsdk.WeChatPayConfig;
import com.google.gson.Gson;

import lombok.extern.slf4j.Slf4j;

@RestController
@RequestMapping("/app/wechat")
@Slf4j
public class WeChatRestController {
	
	@Autowired
	private SmsUtils smsUtils;

	@Autowired
	private WxUserService wxUserService;
	
	@Autowired
    private JwtUtils jwtUtils;
	
	@Autowired
	private AccessTokenStore accessTokenStore;
	
	@Value(value = "${wechat.certPath}")
	private String certPath;
	
	@Autowired
	private WxOrderService wxOrderService;
	
	@Autowired
	private SysConfigService sysConfigService;
	
	/**
	 * 获取微信小程序openId, sessionKey
	 * @param response
	 * @param request
	 */
	@GetMapping("/login")
	public R login(HttpServletResponse response, HttpServletRequest request) {
		
		String code = request.getParameter("code");
		
		log.info("code is {}", code);
		
		if ( StringUtils.isEmpty(code)) {
			throw new RRException(AppResultEnum.CODE_IS_NULL);
		}
		
		String openId = WeChatUtils.getOpenIdByCode(code);
		
		if ( StringUtils.isEmpty(openId)) {
			throw new RRException(AppResultEnum.OPEN_ID_IS_NULL);
		}
		
		EntityWrapper<WxUserEntity> ew = new EntityWrapper<>();
		
		ew.where("openid = {0}", openId);
		
		WxUserEntity wxUserEntity = this.wxUserService.selectOne(ew);
		
		if (wxUserEntity == null ) {
			wxUserEntity = new WxUserEntity();
			wxUserEntity.setOpenid(openId);
			wxUserEntity.setCreatedDate(new Date());
			wxUserEntity.setIsAllianceBusiness(CommonParameters.IS_EFFECT.NO);
			wxUserEntity.setIsVip(CommonParameters.IS_EFFECT.NO);
			
			String inviteUserId = request.getParameter("inviteUserId");
			String qrCodeId = request.getParameter("qrCodeId");
			
			WxInviteEntity wxInviteEntity = new WxInviteEntity();
			if ( !StringUtils.isEmpty(inviteUserId) && !StringUtils.isEmpty(qrCodeId)) {
				try {
					wxInviteEntity.setQrcodeId(Integer.valueOf(qrCodeId));
					wxInviteEntity.setInviteUserid(Integer.valueOf(inviteUserId));
				} catch ( NumberFormatException e) {
					log.error(e.getMessage());
					log.error("qrCodeId: " + qrCodeId + ", invitedUserId: " + inviteUserId);
					wxInviteEntity.setQrcodeId(null);
					wxInviteEntity.setInviteUserid(null);
				}
			}
			
			if ( !StringUtils.isEmpty(inviteUserId)) {
				try {
					wxInviteEntity.setInviteUserid(Integer.valueOf(inviteUserId));
				} catch ( NumberFormatException e) {
					log.error(e.getMessage());
					log.error("invitedUserId: " + inviteUserId);
					wxInviteEntity.setInviteUserid(null);
				}
			}
			
			this.wxUserService.save(wxUserEntity, wxInviteEntity);
		}
		
		//生成token
        String token = jwtUtils.generateToken(String.valueOf(wxUserEntity.getUserid()));

		//生成token，并保存到数据库-缓存中
        return R.ok().put("token", token);
		
	}
	
	@PostMapping("/save/userinfo")
	@Login
	public R saveUserInfo(@RequestBody WxUserEntity wxUserEntity, @RequestAttribute("userId") Integer userId) {
		
		this.wxUserService.updateWeChatUser(wxUserEntity, userId);
		
		return R.ok();
	}
	
	/**
	 * 获取accessToken
	 */
	@GetMapping("/accessToken")
	public R getAccessToken() {
		
		String accessToken = WeChatUtils.getAccessToken();
		
		this.accessTokenStore.setAccessToken(accessToken);
		
		return R.ok();
	}
	
	/**
	 * 统一下单
	 */
	@GetMapping("/order/create")
	@Login
	public R getPrepayId(@RequestAttribute("userId") Integer userId, HttpServletRequest request) {
		
		try {
			
			SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
			
			String orderNum = sdf.format(new Date()) + this.smsUtils.createVerifyCode();
			
			WxUserEntity wxUserEntity = this.wxUserService.selectById(userId);
			
			if ( wxUserEntity.getIsVip().intValue() == CommonParameters.IS_EFFECT.YES ) {
				return R.error();
			}
			
			WXPay wxPay = new WXPay(new WeChatPayConfig(certPath));
			
			Map<String, String> data = new HashMap<>();
	        data.put("body", this.sysConfigService.selectById(ConfigConstant.WX_PAY_BODY).getParamValue());
	        data.put("out_trade_no", orderNum);
	        data.put("fee_type", "CNY");
	        data.put("total_fee", this.sysConfigService.selectById(ConfigConstant.PRODUCT_PRICE).getParamValue());
	        data.put("spbill_create_ip", IPUtils.getIpAddr(request));
	        data.put("notify_url", "https://sjt.gaokaozyt.cn/sjt/app/wechat/receive/pay/result");
	        data.put("trade_type", "JSAPI");
	        data.put("openid", wxUserEntity.getOpenid());
	        String nonceStr = WXPayUtil.generateNonceStr();
	        data.put("nonce_str", nonceStr);
			
			Map<String, String> weChatPayResultMap = wxPay.unifiedOrder(data);
			
			log.info("weChatPayResultMap is {}", weChatPayResultMap);
			
			String unnfiedOrderReturnCode = weChatPayResultMap.get("return_code");
			
			if ( !StringUtils.isEmpty(unnfiedOrderReturnCode) && WeChatUtils.WECHAT_SUCCESS.equals(unnfiedOrderReturnCode)) {
				// 接口调用成功
				String unnfiedOrderResultCode = weChatPayResultMap.get("return_code");
				if ( WeChatUtils.WECHAT_SUCCESS.equals(unnfiedOrderResultCode) ) {
					String prepayId = weChatPayResultMap.get("prepay_id");
					
					WxOrderEntity wxOrderEntity = new WxOrderEntity();
					
					wxOrderEntity.setCreatedDate(new Date());
					wxOrderEntity.setOrderNum(orderNum);
					wxOrderEntity.setStatus(CommonParameters.IS_EFFECT.NO);
					wxOrderEntity.setUserId(userId);
					wxOrderEntity.setPrepayId(prepayId);
					wxOrderEntity.setNotifyStatus(CommonParameters.IS_EFFECT.NO);
					
					this.wxOrderService.insert(wxOrderEntity);
					
					log.info("prepayId is {}", prepayId);
					
					Map<String, String> reqData = new HashMap<>();
					
					reqData.put("appId", WeChatUtils.APP_ID);
					reqData.put("nonceStr", nonceStr);
					reqData.put("package", "prepay_id=" + prepayId );
					reqData.put("signType", WXPayConstants.HMACSHA256);
					Integer timeStamp = (int)(new Date().getTime() / 1000);
					reqData.put("timeStamp", timeStamp + "");
					
					String payRequestSign = WXPayUtil.generateSignature(reqData, WeChatUtils.MCH_KEY, SignType.HMACSHA256);
					reqData.put("sign", payRequestSign);
					
					reqData.remove("appId");
					reqData.remove("key");
					
					R result = R.ok();
					result.putAll(reqData);
					
					return result;
				} else {
					throw new RRException(AppResultEnum.UNINFIED_ORDER_RETURN_CODE_ERROR);
				}
			} else {
				throw new RRException(AppResultEnum.UNINFIED_ORDER_RETURN_CODE_ERROR);
			}
		} catch ( Exception e ) {
			throw new RRException(AppResultEnum.SYSTEM_ERROR);
		}
	}
	
	/**
	 * 接收微信支付成功之后的回调函数
	 */
	@PostMapping("/receive/pay/result")
	public void receivePayResult(HttpServletRequest request, HttpServletResponse response) {
		try {
			InputStream inStream = request.getInputStream();
	        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
	        byte[] buffer = new byte[1024];
	        int len = 0;
	        while ((len = inStream.read(buffer)) != -1) {
	            outSteam.write(buffer, 0, len);
	        }
	        String weChatResultXmlData = new String(outSteam.toByteArray(), "utf-8");
			
	        log.info("weChatResultXmlData is {}", weChatResultXmlData);
	        
			Map<String, String> respData = WXPayUtil.xmlToMap(weChatResultXmlData);
			
			log.info("convert result is {}", respData);
			
			outSteam.close();
	        inStream.close();
	        
	        if ( respData.get("return_code").equals(WeChatUtils.WECHAT_SUCCESS) ) {
	        	if (respData.get("result_code").equals(WeChatUtils.WECHAT_SUCCESS)) {
	        		if ( Integer.parseInt(respData.get("total_fee")) != Integer.parseInt(this.sysConfigService.selectById(ConfigConstant.PRODUCT_PRICE).getParamValue()) ) {
	        			throw new RRException(AppResultEnum.SYSTEM_ERROR);
	        		}
	        		// 支付成功
	        		String preparyId = this.wxOrderService.updateStatusByOrderId(respData.get("out_trade_no"));
	        		// 发送模板消息
	        		
	        		Map<String, Map<String, String>> dataMap = new HashMap<>();
	        		
	        		Map<String, String> valueMap = new HashMap<>();
	        		
	        		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	        		
	        		valueMap.put("value", sdf.format(new Date()));
	        		
	        		dataMap.put("keyword1", valueMap);
	        		
	        		valueMap = new HashMap<>();
	        		
	        		valueMap.put("value", new String("永久会员".getBytes(),"utf-8"));
	        		
	        		dataMap.put("keyword2", valueMap);
	        		
	        		valueMap = new HashMap<>();
	        		
	        		valueMap.put("value", respData.get("out_trade_no"));
	        		
	        		dataMap.put("keyword3", valueMap);
	        		
	        		valueMap = new HashMap<>();
	        		
	        		valueMap.put("value", WeChatUtils.changeF2Y(Integer.parseInt(respData.get("total_fee"))));
	        		
	        		dataMap.put("keyword4", valueMap);
	        		
	        		PurchaseSuccessDTO purchaseSuccessDTO = new PurchaseSuccessDTO(); 
	        		
	        		purchaseSuccessDTO.setData(dataMap);
	        		purchaseSuccessDTO.setForm_id(preparyId);
	        		purchaseSuccessDTO.setTemplate_id(WeChatUtils.PURCHASE_SUCCESS_TEMPLATE_ID);
	        		purchaseSuccessDTO.setTouser(respData.get("openid"));
	        		purchaseSuccessDTO.setAccess_token(this.accessTokenStore.getAccessToken());
	        		
	        		// 模板消息
	        		String sendTemplateMsgResult = WeChatUtils.sendTemplateMsg(purchaseSuccessDTO);
	        		log.info("sendTemplateMsgResult result is {}" , sendTemplateMsgResult);
	        		
	        		Gson gson = new Gson();
	        		
	        		TemplateResultDTO templateResultDTO = gson.fromJson(sendTemplateMsgResult, TemplateResultDTO.class);
	        		
	        		if ( templateResultDTO.getErrcode().intValue() == CommonParameters.IS_EFFECT.NO.intValue()) {
	        			this.wxOrderService.updateNotifyStatusByOrderId(respData.get("out_trade_no"));
	        			
	        			this.wxUserService.updateUserVipStatusByOpenId(respData.get("openid"));
	        		}
		        	
		        	response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>");
	        		
	        	} else {
	        		response.getWriter().write("<xml><return_code><![CDATA[FAIL]]></return_code></xml>");
	        	}
	        } else {
	        	response.getWriter().write("<xml><return_code><![CDATA[FAIL]]></return_code></xml>");
	        }
			
		} catch( Exception e) {
			log.error(e.getMessage());
			
			throw new RRException(AppResultEnum.SYSTEM_ERROR);
		}
	}
	
}
